package com.foreveross.crawl.adapter.sub.impl20140402.v3.elong;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;

import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.adapter.sub.impl20140402.v3.ELongAdapter;
import com.foreveross.crawl.common.util.DateUtil;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.single.SinglePlaneInfoEntity;
import com.foreveross.crawl.enums.RouteTypeEnum;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.taskservice.common.bean.TaskModel;

/**
 * 艺龙国内单程 <br/>
 * 
 * 从页面抓取税费。
 * 
 * @author luomingliang@foreveross.com
 * @date 2014-08-18
 *
 */
public class ElongDomesticOneWayTrip  {
	protected Log logger = LogFactory.getLog(getClass());
	private static final Integer fetch_more_cabin_trycount = 3; // 更多舱位失败重试次数 。
	private static final Boolean is_fetch_more_cabin = Boolean.FALSE; // 是否抓取更多舱位。
	private static final Long more_cabin_interval = 5000L; // 更多舱位抓取间隔时间。
	// private static final Map<String, String> DomesticCabinType = ImmutableMap.of("经济舱", "", "超级/高端经济舱", "economy", "商务/头等", "business");

	// 保存城市名称的拼音
	private String fromPinyin;
	private String toPinyin;
	private ELongAdapter adapter;
	private TaskModel taskQueue;
	/**
	 * ******************************** Constructor ***********************************************
	 **/

	/**
	 * 构造方法，艺龙的请求中用到城市的拼音,由任务那边的城市名中传递过来(例：北京|beijin)
	 * 
	 * @param taskQueue
	 * @throws Exception
	 */
	public ElongDomesticOneWayTrip(TaskModel taskQueue, ELongAdapter adapter) throws Exception {
		this.taskQueue = taskQueue;
		this.adapter = adapter;
		fromPinyin = taskQueue.getFromCityName().split("\\|")[1];
		toPinyin = taskQueue.getToCityName().split("\\|")[1];
		//this.taskQueue.setFromCityName(taskQueue.getFromCityName().split("\\|")[0]);
		//this.taskQueue.setToCityName(taskQueue.getToCityName().split("\\|")[0]);
	}

	/**
	 * ******************************** Fetch ***********************************************
	 **/

	/**
	 * 抓取单程数据。
	 * 
	 * @return
	 * @throws Exception
	 */
	public List<Object> fetchData() throws Exception {
		HttpRequestBase request = null;
		String page = null;
		// 获取数据
		try {
			logger.info("正在抓取数据...");
			request = new HttpGet(getUrl());
			page = adapter.excuteRequest(request); // 抓取数据。
			adapter.appendPageContents(page);// 保存原页面。
			validateFetch(page); // 验证数据。
		} finally {
			if (request != null) request.releaseConnection(); // 释放连接。
		}
		return paraseDomesticOneWay(page);
	}

	/**
	 * 数据请求链接参数拼装 。
	 */
	public String getUrl() throws Exception {
		// 根据航线的不同枚举类型来拼字符串地址
		Map<String, String> p = new HashMap<String, String>();
		p.put("DepartCity", this.speicalCity(this.taskQueue.getFromCity()));
		p.put("DepartCityName", this.taskQueue.getFromCityName());
		p.put("DepartCityNameEn", this.fromPinyin);
		p.put("DepartDate", taskQueue.getFlightDate());
		p.put("DayCount", this.getGapDay(taskQueue.getFlightDate()) + "");
		p.put("BackDayCount", "0");
		p.put("BackDate", taskQueue.getFlightDate());
		p.put("AirCorp", "0");
		p.put("ArriveCity", this.speicalCity(this.taskQueue.getToCity()));
		p.put("ArriveCityName", this.taskQueue.getToCityName());
		p.put("ArriveCityNameEn", this.fromPinyin);
		p.put("ElongMemberLevel", "Common");
		p.put("FlightType", "OneWay");
		p.put("IssueCity", this.speicalCity(this.taskQueue.getFromCity()));
		p.put("OrderBy", "Price");
		p.put("OrderFromId", "2003");
		p.put("PageName", "list");
		p.put("SeatLevel", "Y");
		p.put("_", System.currentTimeMillis() + "");
		p.put("language", "cn");
		p.put("viewpath", "~/views/list/oneway.aspx");
		return adapter.getBaseGetUrl("http://flight.elong.com/isajax/OneWay/S", p);
	}

	/**
	 * ******************************** Parse ***********************************************
	 **/

	/**
	 * 解析国内单程json.
	 * 
	 * @return
	 * @throws Exception
	 */
	private List<Object> paraseDomesticOneWay(String page) throws Exception {
		List<SinglePlaneInfoEntity> entitys = new ArrayList<SinglePlaneInfoEntity>();
		JSONObject value;
		JSONArray flights;
		JSONArray transferLegs;//中转
		JSONObject json = null;
		try {
			json = JSONObject.fromObject(page);
			value = json.getJSONObject("value");
			flights = value.getJSONArray("MainLegs");//主要航班信息
			transferLegs = value.getJSONArray("TransferLegs");//中转航班
			
			if (flights.isEmpty() && transferLegs.isEmpty()) {
				throw new FlightInfoNotFoundException();// 没有航班,抛出FlightInfoNotFoundException。
			}
			if (!flights.isEmpty()) setEntitysInfo(flights, entitys);
			if (!transferLegs.isEmpty()) setEntitysInfo(transferLegs, entitys);
		} finally {
			transferLegs = null;
			flights = null;
			value = null;
		}
		PlaneInfoEntityBuilder.buildLimitPrice(entitys); // 生成最低和最高价格。
		return Arrays.asList(entitys.toArray());
	}

	/**
	 * 解析航班。
	 * 
	 * @param flights
	 * @param entitys
	 * @throws Exception
	 */
	private void setEntitysInfo(JSONArray flights, List<SinglePlaneInfoEntity> entitys) throws Exception {
		JSONObject f;
		JSONArray segs;
		SinglePlaneInfoEntity entity;
		List<TransitEntity> transits = null;
		try {
			for (int i = 0; i < flights.size(); i++) {
				f = flights.getJSONObject(i);
				segs = f.getJSONArray("segs");
				transits = this.parseTrantsInfo(segs);
				
				entity = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, 
						 transits.get(0).getCarrierKey(), transits.get(0).getCarrierName(), transits.get(0).getCarrierFullName(), 
							null, null, transits.get(0).getFlightNo(),
							null, null, null, transits.get(0).getFlightType(), SinglePlaneInfoEntity.class);
				entity.setFromCityName(taskQueue.getFromCityName().split("\\|")[0]);
				entity.setToCityName(taskQueue.getToCityName().split("\\|")[0]);
				entity.setStartTime(transits.get(0).getStartTime());
				entity.setEndTime(transits.get(transits.size()-1).getEndTime());
				long flightDuration = 0;
				for(TransitEntity e : transits){//累加时长，所有中转时长加起来就是总的去程时长
					flightDuration += e.getStayTime();
				}
				entity.setFlightDuration(flightDuration);
				entity.setTotalLowestTaxesPrice(f.getDouble("ofee")+f.getDouble("tax"));
				entity.setTotalLowestPrice(f.getDouble("minp"));
				entity.setSumLowestPrice(entity.getTotalLowestPrice()+entity.getTotalLowestTaxesPrice());
				if(transits.size() >1) entity.getTransits().addAll(transits);//大于１时，才代表有中转
				
				if (is_fetch_more_cabin && transits.size()>1) this.getMoreCabin(entity, f); // 抓取更多舱位。
				
				if(entity.getCabins().isEmpty()){//因为抓取更多舱位时，已经会将刚开始的几个舱位包括进去，所以这里要判断，以防重复设置
					entity.getCabins().addAll(this.parseCabinInfo(f.getJSONArray("cabs")));
				}
				this.parseHighPrice(entity);
				entity.setTotalHighestTaxesPrice(entity.getTotalLowestTaxesPrice());
				entity.setSumHighestPrice(entity.getTotalHighestPrice()+entity.getTotalHighestTaxesPrice());
				
				entitys.add(entity);
			}
		} finally {
			entity = null;
			f = null;
		}
	}
	/**
	 * 解析税费，每一个航班（包括中转）都有一个税费及燃油费,累计所有的即为总税费
	 * @param segs
	 * @return
	 */
	private void parseHighPrice(SinglePlaneInfoEntity entity){
		Double hp = 0d;
		for(CabinEntity e : entity.getCabins()){
			if(e.getPrice()>hp) hp = e.getPrice();
		}
		entity.setTotalHighestPrice(hp);
	}
	/**
	 * 设置舱位
	 * @param arr
	 * @return
	 * @throws Exception
	 */
	private List<CabinEntity> parseCabinInfo(JSONArray arr) throws Exception{
		List<CabinEntity> results = new ArrayList<CabinEntity>();
		CabinEntity cabin = null;
		JSONObject json = null;
		for(int j = 0; j < arr.size(); j++){//一个就是一个航班
			json = arr.getJSONObject(j);
			cabin = PlaneInfoEntityBuilder.buildCabinInfo(json.getString("cname"), json.getString("cab"),
					json.getString("wname"), null, json.getString("price"), json.getString("oprice"), null,
					null, CabinEntity.class);
			results.add(cabin);
		}
		return results;
	}
	/**
	 * 设置中转对象
	 * @param segmentList
	 */
	private List<TransitEntity> parseTrantsInfo(JSONArray arr) throws Exception{
		List<TransitEntity> transits = new ArrayList<TransitEntity>();
		JSONObject json = null;
		TransitEntity transitEntity = null;
		try{
			logger.info(String.format("获得的中转航班数量：%s",arr.size()));
			for(int j = 0; j < arr.size(); j++){//一个就是一个航班
				json = arr.getJSONObject(j);
				transitEntity = PlaneInfoEntityBuilder.buildTransitEntity(json.getString("fltno"), null, 
						json.getString("corp"), json.getString("corpn"), json.getString("corpn"), 
						json.getString("dport"), json.getString("dportn"), json.getString("aport"),
						json.getString("aportn"), json.getString("plane"));
				transitEntity.setStartTime(DateUtil.StringToDate("yyyy-MM-dd HH:mm", json.getString("dtime")));
				transitEntity.setEndTime(DateUtil.StringToDate("yyyy-MM-dd HH:mm", json.getString("atime")));
				transitEntity.setStayTime(Long.parseLong(json.getString("ftime"))*60*1000);
				
				transits.add(transitEntity);
			 }
		}finally{
			json = null; 
			transitEntity = null; 
		}
		return transits;
	}

	/**
	 * ******************************** Fetch Morecabin ***********************************************
	 **/

	/**
	 * 获取更多舱位。
	 * 
	 * @param entity
	 * @param f
	 * @param segment
	 * @param retry 失败重试次数 。
	 */
	private void getMoreCabin(SinglePlaneInfoEntity entity, JSONObject f) {
		HttpRequestBase request = null;
		String json = null;
		JSONObject value = null;
		JSONArray cabins = null;
		JSONArray segs = null;
		CabinEntity cabinEntity;
		JSONObject v;
		int retry = fetch_more_cabin_trycount;
		while(retry-- > 0){
			// /获取数据。
			try {
				logger.error(String.format("正在抓取[%s]的更多舱位.", entity.getFlightNo()));
				request = adapter.getBasePost("http://flight.elong.com/isajax/OneWay/GetMorePrices", getMoreParams(entity, f));
				json = adapter.excuteRequest(request, more_cabin_interval); // 执行间隔5秒
				adapter.appendPageContents(json);
				value = JSONObject.fromObject(json).getJSONObject("value");
			} catch (Exception e) {
				if (retry > 0) {
					logger.error(String.format("航班[%s]更多舱位获取失败，现在更换IP重试：", entity.getFlightNo(), e.getMessage()));
					adapter.switchProxyip();
				} else {
					logger.error("更多舱位获取失败 !" + e.getMessage());
					return;
				}
			} finally {
				if (request != null) {
					request.releaseConnection();
				}
			}
		}
		// /解析数据。
		try {
			logger.error(String.format("[%s]舱位数量:%s",entity.getFlightNo(),  cabins.size()));
			Double taxes = 0d;
			segs = value.getJSONObject("FlightLegList").getJSONArray("segs");
			for(int i = 0; i < segs.size(); i++){
				taxes += segs.getJSONObject(i).getDouble("ofee")+segs.getJSONObject(i).getDouble("tax");
			}
			cabins = value.getJSONObject("FlightLegList").getJSONArray("cabs");
			logger.error(String.format("舱位数量:%s", cabins.size()));
			for (int i = 0; i < cabins.size(); i++) {
				v = cabins.getJSONObject(i);
				cabinEntity = PlaneInfoEntityBuilder.buildCabinInfo(v.getString("cname"), v.getString("cab"), v.getString("wname"),
						taxes.toString(),
						v.getString("price"), v.getString("oprice"), null, null,
						CabinEntity.class);
				entity.getCabins().add(cabinEntity);
			}
		} catch (Exception e) {
			logger.error("更多舱位解析失败：" + e.getMessage());
		} finally {
			v = null;
			cabinEntity = null;
			value = null;
			json = null;
		}
	}

	/**
	 * 拼装更多舱位的请求参数 。
	 * 
	 * @param entity 当前航班实体。
	 * @param f
	 * @param segment
	 * @return
	 * @throws Exception
	 */
	private Map<String, String> getMoreParams(SinglePlaneInfoEntity entity, JSONObject f) throws Exception {
		Map<String, String> p = new HashMap<String, String>();
		p.put("arrivecitynameen", this.toPinyin);
		p.put("channel", "4");
		p.put("daycount", this.getGapDay(taskQueue.getFlightDate()) + "");
		p.put("departcitynameen", this.fromPinyin);
		p.put("flightNumber", entity.getFlightNo());
		p.put("flighttype", "0");
		p.put("language", "cn");
		p.put("pagename", "list");
		p.put("request.AirCorp", "0");
		p.put("request.ArriveCity", this.speicalCity(taskQueue.getToCity()));
		p.put("request.ArriveCityName", taskQueue.getToCityName());
		p.put("request.ArriveCityNameEn", this.toPinyin);
		p.put("request.BackDate", taskQueue.getFlightDate());
		p.put("request.BackDayCount", "0");
		p.put("request.DayCount", this.getGapDay(taskQueue.getFlightDate()) + "");
		p.put("request.DepartCity", this.speicalCity(taskQueue.getFromCity()));
		p.put("request.DepartCityName", taskQueue.getFromCityName());
		p.put("request.DepartCityNameEn", this.fromPinyin);
		p.put("request.DepartDate", taskQueue.getFlightDate());
		p.put("request.ElongMemberLevel", "Common");
		p.put("request.FlightType", "OneWay");
		p.put("request.IssueCity", this.speicalCity(taskQueue.getFromCity()));
		p.put("request.OrderBy", "Price");
		p.put("request.OrderFromId", "2003");
		p.put("request.PageName", "list");
		p.put("request.SeatLevel", "Y");
		p.put("request.language", "cn");
		p.put("request.viewpath", "~/views/list/oneway.aspx");
		p.put("seatlevel", "Y");
		p.put("uniquekey", entity.getFlightNo() + "ShopS" + f.getString("minp"));
		p.put("viewpath", "~/views/list/oneway.aspx");
		return p;
	}

	/**
	 * ******************************** Utils ***********************************************
	 **/

	/**
	 * 验证抓取的数据。
	 */
	public boolean validateFetch(Object fetchObject) throws Exception {
		if (adapter.getRouteType() == RouteTypeEnum.DOMESTIC_ONEWAYTRIP) {
			if (!fetchObject.toString().contains("\"success\":true")) {
				throw new Exception("获取的数据不正确");
			}
		} else {
			if (fetchObject == null || fetchObject.toString().length() < 20000) {
				throw new Exception("艺龙网页数据不正确或者网页为空");
			}
			String page = fetchObject.toString().replaceAll("\\s", "");
			if (page.matches(".*notfoundontheRomPagerserver.*") || page.matches(".*DieSeitekannnichtangezeigtwerden.*") || page.matches(".*400BadRequest.*") || page.matches(".*wasnotfoundonthisserver.*") || page.matches(".*403Forbidden.*")
					|| page.matches(".*wasnotfoundontheRomPagerserver.*") || page.matches(".*Thetargetserverfailedtorespond.*")) {
				throw new Exception("艺龙网页数据有非法错误提示信息");
			}
		}
		return true;
	}

	/**
	 * 处理机场三字码。
	 * 
	 * @param cityCode 机场三字码。
	 * @return 转换后的机场三字码。
	 */
	// 专门处理特别航班数据的，目前已知的就是北京问题
	private String speicalCity(String cityCode) {
		if ("PEK".equals(cityCode)) {
			return "BJS";
		} else if ("PVG".equals(cityCode)) {
			return "SHA";
		}
		return cityCode;
	}

	/**
	 * 获取起飞时间获取与当前的间隔时间。
	 * 
	 * @param flightDateStr 飞行时间 。
	 * @return 起飞时间获取与当前的间隔时间。
	 * @throws Exception
	 */

	private int getGapDay(String flightDateStr) throws Exception {
		long oneDay = 24 * 60 * 60 * 1000;
		Date now = new Date();
		now.setHours(0);
		now.setMinutes(0);
		now.setSeconds(0);
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
		Date flight = sf.parse(flightDateStr);
		long chaTime = flight.getTime() - now.getTime();
		long chaDay = chaTime / oneDay;
		chaDay = chaTime % oneDay > 0 ? chaDay + 1 : chaDay;
		return (int) chaDay;
	}

}
